<?php
/**
 * 开放平台接收微信公众号消息事件处理控制器
 *
 * Author: Zeon
 * Date: 2018/4/3
 * Created by Panxsoft.
 */
namespace App\Http\Controllers\OpenWechat;

use App\Exceptions\ApiErrCode;
use App\Http\Controllers\Controller;
use App\Http\Requests\OpenWechat\ServerRequest;
use App\Models\OpenWechat\Authorizer;
use App\Models\OpenWechat\WechatUser;
use App\Services\OpenWechat\AppFactory;
use App\Services\OpenWechat\Contracts\AppServer;
use App\Services\OpenWechat\EventMessageHelper;
use App\Services\OpenWechat\Mock;
use App\Services\OpenWechat\ScanEventHandler;
use App\Services\OpenWechat\Servers\PublicDebutTestServer;
use App\Services\OpenWechat\WechatUserRepository;
use EasyWeChat\OfficialAccount\Application;

class ServerController extends Controller
{
    const ROUTES = [
        'serve' => 'open-wechat.serve'
    ];

    /**
     * @var Application - 如在测试环境中不要使用，无法保证 ip 与 token
     */
    protected $app;

    /**
     * @var Authorizer
     */
    protected $authorizer;

    protected $debug = false;

    const WHO_AM_I = 'whoami';

    /**
     * 对接微信公众号消息
     *
     * @param $authorizer_appid
     * @param ServerRequest $request
     *
     * @return null
     * @throws \Exception
     */
    public function serve($authorizer_appid, ServerRequest $request)
    {
//        info(ApiErrCode::LOG_PREFIX. 'OpenWechat Server Event Appid:' . $authorizer_appid);

        // FOR open platform debut or other customize develop
        $customize_server = $this->registerServerByAppId($authorizer_appid);
        if ($customize_server) {
            return $customize_server->serve();
        }

        $this->authorizer = Authorizer::findByAppId($authorizer_appid);

        // FOR phpunit or local debug
        $this->debug = $request->isDebug();
        if ($this->debug) {
            return $this->tryHandleMessage($request->fakeMessage());
        }

        // Real Server Handler
        $this->app = (new AppFactory())->authorizerApp($authorizer_appid);
        $server = $this->app->server;
        $server->push(function (array $message){
            return $this->tryHandleMessage($message);
        });
        $response = $server->serve();

        return $response;
    }

    /**
     * @param array $message
     * @return null|string
     */
    private function tryHandleMessage(array $message)
    {
        try {
            return $this->handleMessageByType($message);
        } catch (\Exception $exception) {
            if (config('app.debug') || config('app.open_wechat.debug')) {
                return $exception->getFile() . ": " . $exception->getLine() . ": " . $exception->getMessage();
            }

            return trans('open_wechat.notice_error');
        }
    }

    /**
     * @param $message
     *
     * @return mixed
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    private function handleMessageByType(array $message)
    {
        /**
         * @link https://www.easywechat.com/docs/master/zh-CN/official-account/server - 消息类型
         */
        switch ($message['MsgType']) {
            case 'event':
                return $this->handleEventMessage($message);
            case 'text':
                return $this->handleTextMessage($message);
            case 'image':
                break;
            case 'voice':
                break;
            case 'video':
                break;
            case 'location':
                break;
            case 'link':
                break;
            default:
                break;
        }

        return null;
    }

    /**
     * @param array $message
     * @see Mock::serverEventScanMessage()
     * @return mixed
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function handleEventMessage(array $message)
    {
        switch ($message['Event']) {

            // 扫码订阅或普通关注事件
            case 'subscribe':
                return $this->handleSubscribeEvent($message);

            // 纯粹的扫码事件
            case 'SCAN' :
                return $this->handleScanEvent($message);

            //自定义菜单的处理
            case 'CLICK':
//                return $this->customClickHandler($message);
                break;

            // 点击菜单 VIEW 也会触发回调事件
            case 'VIEW':
//                return $this->handleViewEvent($message);
                break;

            // 取关
            case 'unsubscribe':
                return $this->handleUnSubscribeEvent($message);

            // 地理位置
            case 'LOCATION' :
                return $this->handleLocationEvent($message);

            // 门店 WIFI 已连接
            case 'WifiConnected' :
//                return $this->handlePOIWifiConnectedEvent($message);
                break;

            // '其它事件';
            default:
                return null;
        }
    }

    /**
     * @param $message
     * @see Mock::serverEventSubscribeMessage()
     * @return mixed|null|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    private function handleSubscribeEvent(array $message)
    {
        $this->saveSubscribeWechatUser($message);

        if (! isset($message['EventKey'])) {
            //纯粹的关注事件;
            return $this->handlePureSubscribeEvent($message);
        } else {
            // 扫码关注事件
            return $this->processQrCodeScan($message);
        }
    }

    /**
     * @param array $message
     * @return WechatUser
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    private function saveSubscribeWechatUser(array $message)
    {
        $user_repo = new WechatUserRepository();

        $user_info = $this->app->user->get($message['FromUserName']);

        $user_info['authorizer_id'] = $this->authorizer->id;

        $wechat_user = $user_repo->createOrUpdateWechatUserInfo($user_info);

        return $wechat_user;
    }


    /**
     * 关注公众号，保存微信用户信息，返回关注公众号的信息
     *
     * @param $message
     * @see Mock::serverEventSubscribeMessage()
     *
     * @return mixed|string
     */
    private function handlePureSubscribeEvent(array $message)
    {
        return null;
//        return trans('open_wechat.notice_welcome');
    }

    /**
     * 处理微信用户扫码带的参数
     *
     * @param $message
     * @see Mock::serverEventScanMessage()
     * @see Mock::serverEventScanSubscribeMessage()
     *
     * @return null|string
     */
    private function processQrCodeScan(array $message)
    {
        $event_key = EventMessageHelper::normalizeEventKey($message['EventKey']);

        $scan_event_handler = ScanEventHandler::registerEventKey($event_key);
        if (! $scan_event_handler) {
            return $this->debugLogQrScanEvent($message);
        }

        return $scan_event_handler->response($message);
    }

    /**
     * @param array $message
     */
    private function debugLogQrScanEvent(array $message)
    {
        if (! config('pkx.open_wechat.debug')) return;
        
        logger(ApiErrCode::LOG_PREFIX . 'Undefined EventKey Found');

        logger($message);

        return;
    }
    /**
     * @param $message
     * @return null|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    private function handleScanEvent(array $message)
    {
        if (! $this->debug) { // should not query save user when local debug
            $wechat_user = $this->saveSubscribeWechatUser($message);
            if (! $wechat_user) {
                return trans('open_wechat.scan_user_not_found');
            }
        }

        return $this->processQrCodeScan($message);
    }

    /**
     * @param string $authorizer_appid
     * @return AppServer
     */
    private function registerServerByAppId($authorizer_appid)
    {
        switch ($authorizer_appid) {
            case Authorizer::PUBLIC_DEBUT_TEST_APPID :
                return new PublicDebutTestServer();
            default :
                return null;
        }
    }

    /**
     * @param array $message
     * @see Mock::serverEventUnSubscribeMessage()
     *
     * @return null
     */
    private function handleUnSubscribeEvent(array $message)
    {
        $openid = $message['FromUserName'];

        $un_sub = WechatUser::unSubscribeByOpenId($openid) ? "ok" : "failed";

        return "$openid unSubscribed $un_sub";
    }

    /**
     * @see Mock::serverTextMessage()
     *
     * @param array $message
     * @return string
     */
    private function handleTextMessage(array $message): ?string
    {
        $content = $message['Content'];

        switch ($content) {
            case trans('open_wechat.text_content') :
                $url = $this->authorizer->wechatUserCenterUrl();
                return "<a href=\"{$url}\">".trans('open_wechat.personal_center')."</a>";

            case self::WHO_AM_I :
                return $this->debugMessageResponse($message);
        }

        return null;
    }

    /**
     * @see Mock::serverLocationEventMessage()
     *
     * @link https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140841
     *
     * @param array $message
     *
     * @return string
     */
    private function handleLocationEvent(array $message): ?string
    {
        $openid = $message['FromUserName'];
        
        $wechat_user = WechatUser::findByOpenId($openid);
        if ($wechat_user) {
            $wechat_user->update([
                'latitude'  => $message['Latitude'],
                'longitude' => $message['Longitude'],
            ]);
        }

        return null;
    }

    /**
     * @param array $message
     *
     * @return string
     */
    private function debugMessageResponse(array $message): string
    {
        $openid = $message['FromUserName'];

        return <<<MESSAGE
appid: {$this->authorizer->authorizer_appid}
openid: $openid
MESSAGE;
    }
}
